#ifndef PUB_LIB_H
#define PUB_LIB_H

#include "raylib.h"
#include "raymath.h"
#include "FreeImage.h"

#ifdef WINDOWS
#define sleep Sleep
#define debug_path "..\\res\\image\\RavMon.bmp"
#define mm_path "..\\res\\image\\mm.bmp"
#define debug_tif_path "..\\res\\image\\Fig0333(a)(test_pattern_blurring_orig).tif"

#define IMAGE_PATH "..\\res\\image\\"

#else
#include <unistd.h>

#define debug_path "../res/image/RavMon.bmp"
#define mm_path "../res/image/mm.bmp"
#define debug_tif_path "../res/image/Fig0333(a)(test_pattern_blurring_orig).tif"

#define IMAGE_PATH "../res/image/"

#endif

//for avoiding Division by zero error， divisor add it
#define MIN_NUMBER (expf(-6))

#ifdef WINDOWS
	#define build_complex(r, i)  _FCbuild(r, i)
#else
	#define build_complex(r, i) ((r) + (i) * I)
#endif

//binary image, 0 or 1
#define PIXELFORMAT_UNCOMPRESSED_GRAYSCAL_1 (PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA + 1)

/* full = path/file_name
 * e.g. "../res/image/RavMon.bmp"
 * */
extern void get_full_path(char* path, char* file_name, char* full);

#define MAX_HINT_STRING_LEN 40

#ifndef WINDOWS
#define max(a,b) (a < b ? b : a)
#define min(a,b) (a < b ? a : b)
#endif

//swap 2 float number
#define swapf(a, b) {\
	float tempf = a; \
	a = b; \
	b = tempf; \
}

//swap 2 int number
#define swapi(a, b) {\
	int temp = a; \
	a = b; \
	b = temp; \
}

/* other */
#define ESC_KEY 27

/*
 * 一般选用 GPU 或 CPU 方式修改图片
 *
 * 用GPU 方式修改图片
 * basic.c: 各种初级特效，扫描，百叶窗，马赛克等
 * gtransform.c：几何变换
 * */
typedef struct{
	Image ori; //原始图片信息
	//GPU 处理
	Image chg; //修改后的图片
	Texture2D texture; //修改后的图片

	int image_size; //当前图片的数据部分有多少字节
	int pix_len; //default is 3，每个像素占几个字节
	int order; //当前特效下，还有子特效，子特效的序列号
	int start_pos; //可以定义不同含义，默认值为0
}my_image;

typedef enum{
	RIGHT_IMAGE_FILE = 0, //point to FIBITMAP
	RIGHT_IAMGE_DATA, //point to image data
	RIGHT_IMAGE_HISTO //draw_histogram
}right_image_type;

//CPU 处理
typedef struct {	
	/* Color.a : if set 255, 当前像素有效, should draw it
	 * default Color.a is 0, no need care */
	Color* frame_buf;// rgba
	int fb_pixels; //FrameBuf像素的数目
	int win_width; //FrameBuf 宽度
	int win_height; //FrameBuf 高度

	int image_width;
	int image_height;
	int format; //图像格式
	int bpp; //8, 32, etc.
	int image_size; //当前图片的数据部分有多少字节
	int pix_len; //每个像素占几个字节

	int order; //当前特效下，还有子特效，子特效的序列号

	/*
	 * 以下4个指针都是指向FIBITMAP*
	 * 打开一副图片，最多需要5个 FIBITMAP*，最少需要2个FIBITMAP*
	 * ori, rchg, 并且有一个额外的数据缓冲保存中间计算结果。
	 * float rchg_data[h][w]，存放计算的中间结果，最终会写入 rchg
	 *
	 * ori 指向原始图片
	 * left: 左边小图，如果原始图片足够小，left = ori
	 * right: 右边大图,如果原始图片足够小，right = ori
	 * 以上3个都是原始数据，不过可能经过缩放
	 * rchg: 右边大图经过算法修改过的数据，用于最终显示,
	 *		这个指针初始时一定是新的，从 right 复制的
	 * rchg_data：存放算法的中间结果，大小和右边图的大小相同
	 * */
	FIBITMAP* ori; //original image
	FIBITMAP* chg; //same size of original
	FIBITMAP* left; //left image, small, 可能指向 ori
	FIBITMAP* right; //right image, large, 可能指向 ori

	FIBITMAP* rchg; //right image changed，右图转换后的结果
	FIBITMAP* pri; //copied from right image

	int rw, rh; //width and heitht of right window image
	float* rchg_data;//float data[rh][rw]，存放算法中间结果
	unsigned char* ori_data; //ori[h][w]
	unsigned char* chg_data; //chg[h][w]
	unsigned char* rchg_image; //image[rh][rw]

	unsigned int chg_histo[256]; //直方图数据
	unsigned int ori_histo[256];
	unsigned char sk_data[256]; //变换函数
	int show_histo; //only for show

	int cmd; //previous command

/* 为了显示 */
	int right_window_num; //1 or 4
	int left_max; //左边小图的最大尺寸, win_width*0.3
	int right_max; //右边区域的最大尺寸, win_width*0.7
	int right_image_max; //右边图片的最大尺寸，如果是1张图，则等于right_max

	int startx, starty;
	//左边小图在屏幕的位置
	int lx;
	//右边图在屏幕的位置
	int rx;
	int split_line; //中间分割线的x坐标, y = starty
}cpu_image;

#define sampled_point(color) (color.a)

typedef enum {
	STATE_IDLE = 0,
	STATE_ONGOING,
	STATE_WAIT_NEXT,
	STATE_FINISH
}run_state;

typedef enum {
	NORMAL_POINT = 0,
	ZERO_POINT
}point_type;

typedef struct {
	unsigned char index;
	point_type type;
}broke_line;

#if defined(__cplusplus)
extern "C" {            // Prevents name mangling of functions
#endif

extern void UpdateProgress(float progress);

/* x，y 值最大只能是 max
 * 如果不是，则计算出新的newx, newy
 * 保证 newx，newy 最大只能是max，
 * 并且是等比例变换
 * */
extern void cal_scaled_val(int max, int x, int y, int *xnew, int *ynew);

/* 2的整数次幂 */
extern void cal_scaled_val_radix2(int max, int x, int y, int* xnew, int* ynew);

/*
 * 左边小图：占据宽度的30%, 右边大图：占据宽度的70%
 * 这里取整，返回取整后的最大宽度
 * */
extern int get_max_pixel(int width, float percent);

/* 右边的窗口分为2半 
 * 返回取整后的最大宽度 */
extern int split_right_window(int width, float percent);

extern void clear_fb_range(Color* frame_buf, int fb_width, \
	int startx, int starty, \
	int endx, int endy, Color c);

extern void clear_fb(cpu_image* ptr, Color c);
extern void clear_fb_right(cpu_image* ptr, Color c);

//how many bytes of one pixel occupy
//w: image width, h: image height
extern int get_pixel_bytes(int w, int h, int image_size);
extern void reset_frame_buf(Color* buf, int len, Color c);
extern Color makeRGBA(void* r);

/* CPU render 
 * width, height: screen width, height
 * */
 /*
 * 根据分段函数，折线定义的 points
 * 计算出需要的直方图数据
 */
extern void get_match_histogram(unsigned int* chg_histo);

extern void print_histo_data(unsigned int* histo);
extern void draw_image(Color* frame_buf, int width, int height);

extern void show_ori_image(cpu_image* ptr, int startx, int starty);

extern void draw_histogram(Color* frame_buf, int fb_width, \
	int startx, int starty, \
	int endx, int endy, unsigned int* histo);


extern double twoModeGauss(double r);

/* 画出直方图的变换函数 */
extern void draw_his_trans_func(Color* frame_buf, int fb_width, \
	int startx, int starty, \
	int endx, int endy, unsigned char* sk);

/*
 *  line_bytes: 一行的像素一共有多少字节
 *  从原始图像的缓存中得到索引值
 *  width: 图片的宽度
 * */
extern int get_image_index(int y, int x, int width, int pix_len);

//width: 窗口的宽度
extern int get_window_index(int y, int x, int width);

extern void print_info_cn(const char ary[][MAX_HINT_STRING_LEN], int n);
extern void show_info_en(const char ary[][MAX_HINT_STRING_LEN], int n, int x, int y);

/* 画右边窗口
 * ptr: effect_data 
 * data: image data
 * pos: 1, 2, 3, 4
 * */
extern void draw_right_window(cpu_image *ptr, void *data, int pos,\
		right_image_type data_type);

#define draw_right_image(data, pos) \
	draw_right_window(&effect_data, (void*)(data), (pos), RIGHT_IMAGE_FILE)

#define draw_right_data(data, pos) \
	draw_right_window(&effect_data, (void*)(data), (pos), RIGHT_IAMGE_DATA)

#define draw_right_histo(data, pos) \
	draw_right_window(&effect_data, (void*)(data), (pos), RIGHT_IMAGE_HISTO)

extern void draw_figure_title(cpu_image* ptr, char* str, int pos);
#define draw_title(str, pos) \
	draw_figure_title(&effect_data, str, pos)

/*
 * float image[h][w]
 * char  ori[ori_h][ori_w]
 * */
extern void fill_zero_char(float* image, int w, int h,\
	char* ori, int ori_w, int ori_h);

extern void fill_zero_byte(float* image, int w, int h,\
	unsigned char* ori, int ori_w, int ori_h);

extern void fill_zero_float(float* image, int w, int h,\
	float* ori, int ori_w, int ori_h);

extern //image[Q][P] to cut_image[h][w]
void restore_ori_image(float* image, int P, int Q,
	float* cut_image, int w, int h);

/* normalize image to [0, 1]
 * save result to out[h][w]
 * */
extern void normalize_image(FIBITMAP* dib, float* out);


#if defined(__cplusplus)
}
#endif

#endif


